#pragma once

#include "Types.h"
#include "DLL.h"

#include <deque>
#include <stack>
#include <vector>

namespace My {

class DLL_API Action {
	friend class ActionQueue;
public:
	Action(); 
	virtual ~Action();
public:
	/// Returns true if this action succeeded
	virtual bool Execute() = 0;
	/// Puts the world into the state it was before the Execute() call
	virtual void Unexecute() = 0;
	virtual void Reexecute()
		{ VERIFY(Execute()); }
	virtual String GetCaption() const = 0;
protected:
	void SetModify(bool on) 
		{ m_ModifyInc = on ? + 1 : 0; }
protected:
	int						m_ModifyInc;
	std::vector<Action *>	m_SubActions;
};

class DLL_API MultiActionImpl : public Action {
	friend class ActionQueue;
private:
	/// This action is created internally by the ActionQueue
	MultiActionImpl(const String & caption);
	~MultiActionImpl();
public:
	virtual bool Execute() { return true; }
	virtual void Unexecute() { } 
	virtual String GetCaption() const;
private:
	String	m_Caption;
};

class DLL_API MultiAction {
public:
	// Begins a multi-action
	MultiAction(const String & caption = String());
	// Ends the multi-action
	~MultiAction();
};

class Doc;

class DLL_API ActionQueue {
public:
	ActionQueue(Doc &);
	~ActionQueue();
	/// Returns true if no actions in the queue
	bool Empty() const;
	/// Returns maximum number of actions in the queue
	Size Capacity() const;
	/// Returns actual number of actions in the queue
	Size Size() const;
	/// Returns ith action
	Action * operator [](int index) const;
	/// Returns index of the current action
	int Position() const;
	/// Returns action that can be undone (can be 0)
	Action * Undoable() const;
	/// Returns action that can be redone (can be 0)
	Action * Redoable() const;
	/// Sets the capacity
	void SetCapacity(unsigned int cap);
	/// Removes all actions from the queue
	void Clear();
	/// Tries to execute the given action; if succeeds, puts it into the queue and returns true
	bool Perform(Action * pAction);
	/// Undoes current action, if any
	void Undo();
	/// Redoes previously undone action, if any
	void Redo();
	/// Marks current state as "non-modified" state
	void NonModified();
	/// A non-undoable modification occurred
	void Modified();
	/// Returns true if the document is modified
	bool IsModified() const;
private:
	friend class MultiAction;
	class Block;
	friend class Block;
	void BeginMultiAction(const String & caption);
	void EndMultiAction();
private:
	void Push(Action *);
	Action * Pop();
	void Unexecute(Action *);
	void Reexecute(Action *);
	void Changed();
private:
	unsigned int			m_Capacity;
	std::deque<Action * >	m_Deque;
	unsigned int			m_Position;
	int						m_ModifyCount;
	int						m_BlockCount;
	Doc &					m_Owner;
	std::stack<Action *>	m_Active;
};

} // My

#include "Impl/Action.inl"
